#include "Druide.h"

#include <iomanip>

#include "lib.h"

#include "ATL.h"

#include <algorithm>

using namespace std;
using namespace boost::assign;

const unsigned Druide::nbrCompetences = 75;

const unsigned Druide::nbrRestrictions = 0;

const unsigned Druide::ppAlloues = 100;

/**
 * Les homs des sphères sont accentuées "à la LaTeX", afin de
 * simplifier la gestion des noms de dossiers.
 */
const vector<CompetenceClasse> Druide::Comp = list_of
  (CompetenceClasse("Amitié avec les animaux"                    , 5 ))
  (CompetenceClasse("Communication avec les créatures sylvestres", 10))
  (CompetenceClasse("Déplacement silencieux"                     , 5 ))
  (CompetenceClasse("Dés de vie à 1d10"                          , 10))
  (CompetenceClasse("Identification"                             , 5 ))
  (CompetenceClasse("Immunité aux charmes (niv 7)"               , 5 ))
  (CompetenceClasse("Immunité aux charmes"                       , 10))
  (CompetenceClasse("Immunité aux maladies"                      , 10))
  (CompetenceClasse("Langue des initiés"                         , 5 ))
  (CompetenceClasse("Maîtrise élémentiare"                       , 5 ))
  (CompetenceClasse("Changement de forme (dès niv 5)"            , 15))
  (CompetenceClasse("Changement de forme (niv 7)"                , 10))
  (CompetenceClasse("Passage sans traces"                        , 5 ))
  (CompetenceClasse("Purification de l'eau"                      , 5 ))
  (CompetenceClasse("Résistance au feu et à l'électricité"       , 5 ))
  (CompetenceClasse("Résistance au froid"                        , 5 ))
  (CompetenceClasse("Se cacher dans l'ombre"                     , 5 ))
  (CompetenceClasse("Spécialisation"                             , 15))
  (CompetenceClasse("Sélection de sphères standard"              , 60))
  // -> 19
  (CompetenceClasse("Générale (min)"    , "Generale"             , 3 ))
  (CompetenceClasse("Animale (min)"     , "Animale"              , 5 ))
  (CompetenceClasse("Astrale (min)"     , "Astrale"              , 3 ))
  (CompetenceClasse("Chaos (min)"       , "Chaos"                , 5 ))
  (CompetenceClasse("Charme (min)"      , "Charme"               , 5 ))
  (CompetenceClasse("Climat (min)"      , "Climat"               , 3 ))
  (CompetenceClasse("Combat (min)"      , "Combat"               , 5 ))
  (CompetenceClasse("Création (min)"    , "Creation"             , 5 ))
  (CompetenceClasse("Divination (min)"  , "Divination"           , 5 ))
  (CompetenceClasse("Elémentaire (min)" , "Elementaire"          , 8 ))
  (CompetenceClasse("Air (min)"         , "Air"                  , 2 ))
  (CompetenceClasse("Eau (min)"         , "Eau"                  , 2 ))
  (CompetenceClasse("Feu (min)"         , "Feu"                  , 3 ))
  (CompetenceClasse("Terre (min)"       , "Terre"                , 3 ))
  (CompetenceClasse("Garde (min)"       , "Garde"                , 3 ))
  (CompetenceClasse("Guerre (min)"      , "Guerre"               , 3 ))
  (CompetenceClasse("Invocation (min)"  , "Invocation"           , 5 ))
  (CompetenceClasse("Loi (min)"         , "Loi"                  , 5 ))
  (CompetenceClasse("Nécromancie (min)" , "Necromancie"          , 5 ))
  (CompetenceClasse("Nombres (min)"     , "Nombres"              , 5 ))
  (CompetenceClasse("Pensée (min)"      , "Pensee"               , 5 ))
  (CompetenceClasse("Protection (min)"  , "Protection"           , 5 ))
  (CompetenceClasse("Soin (min)"        , "Soin"                 , 5 ))
  (CompetenceClasse("Soleil (min)"      , "Soleil"               , 3 ))
  (CompetenceClasse("Temps (min)"       , "Temps"                , 5 ))
  (CompetenceClasse("Végétale (min)"    , "Vegetale"             , 5 ))
  (CompetenceClasse("Vigilance (min)"   , "Vigilance"            , 5 ))
  (CompetenceClasse("Voyageur (min)"    , "Voyageur"             , 3 ))
  // -> 47
  (CompetenceClasse("Générale (maj)"    , "Generale"             , 5 ))
  (CompetenceClasse("Animale (maj)"     , "Animale"              , 10))
  (CompetenceClasse("Astrale (maj)"     , "Astrale"              , 6 ))
  (CompetenceClasse("Chaos (maj)"       , "Chaos"                , 8 ))
  (CompetenceClasse("Charme (maj)"      , "Charme"               , 10))
  (CompetenceClasse("Climat (maj)"      , "Climat"               , 10))
  (CompetenceClasse("Combat (maj)"      , "Combat"               , 10))
  (CompetenceClasse("Création (maj)"    , "Creation"             , 10))
  (CompetenceClasse("Divination (maj)"  , "Divination"           , 10))
  (CompetenceClasse("Elémentaire (maj)" , "Elementaire"          , 20))
  (CompetenceClasse("Air (maj)"         , "Air"                  , 5 ))
  (CompetenceClasse("Eau (maj)"         , "Eau"                  , 5 ))
  (CompetenceClasse("Feu (maj)"         , "Feu"                  , 8 ))
  (CompetenceClasse("Terre (maj)"       , "Terre"                , 8 ))
  (CompetenceClasse("Garde (maj)"       , "Garde"                , 5 ))
  (CompetenceClasse("Guerre (maj)"      , "Guerre"               , 5 ))
  (CompetenceClasse("Invocation (maj)"  , "Invocation"           , 10))
  (CompetenceClasse("Loi (maj)"         , "Loi"                  , 8 ))
  (CompetenceClasse("Nécromancie (maj)" , "Necromancie"          , 10))
  (CompetenceClasse("Nombres (maj)"     , "Nombres"              , 10))
  (CompetenceClasse("Pensée (maj)"      , "Pensee"               , 10))
  (CompetenceClasse("Protection (maj)"  , "Protection"           , 10))
  (CompetenceClasse("Soin (maj)"        , "Soin"                 , 10))
  (CompetenceClasse("Soleil (maj)"      , "Soleil"               , 5 ))
  (CompetenceClasse("Temps (maj)"       , "Temps"                , 10))
  (CompetenceClasse("Végétale (maj)"    , "Vegetale"             , 10))
  (CompetenceClasse("Vigilance (maj)"   , "Vigilance"            , 10))
  (CompetenceClasse("Voyageur (maj)"    , "Voyageur"             , 5));
// -> 75

const vector< Prerequis > Druide::Limites = list_of
  (Prerequis(4, 12))
  (Prerequis(5, 15));

const set< string > Druide::RacesAdmises = list_of
  ( "Humain" ) ( "Demi-Elfe" );

/** Constructeur usuel.
 * Permet la construction d'un clerc de A à Z, càd on détermine ici
 * les restrictions, puis les compétences.
 *
 * @param[in] xp le nombre de pts d'expérience du Druide.
 * @param[in] trait les traits du Druide, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in,out] PP nombre de points de personnage (PP) à disposition
 * pour la création du Druide.
 * @param[in] race nom de la race du personnage.
 */
Druide::Druide(unsigned xp, const vector< unsigned >& trait,
	       unsigned& PP, const string& race)
  : Classe( xp, Pretres, "Druide" )
{
  //NomClasse = "Druide";
  NombrePPsAlloues = ppAlloues;
  PP += NombrePPsAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  blank() << "********************************************************" << endmsg;
  blank() << "Création d'un druide" << endmsg << endmsg;

  construitClasse( PP, trait, race );

  blank() << endmsg << setfill('*') << setw(56) << '*' << setfill(' ')
	      << endmsg;
  calculNiveau();
  calculNivMag();
}

/** Constructeur simplifié.
 * Permet de construire un Druide lors de la lecture d'un ficier xml.
 *
 * @param[in] xp le nombre de pts d'expérience du Druide.
 * @param[in] trait les traits du Druide, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] race nom de la race du personnage.
 */
Druide::Druide( unsigned xp )
  : Classe( xp, Pretres, "Druide" )
{
  //NomClasse = "Druide";

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/** Constructeur pour le mode graphique.
 * Permet de construire une instance de Druide avec le contenu
 * minimal, le nombre de points d'expériences et les capacités étant
 * déterminées après la déclaration.
 *
 * @param[in] UI contrôle, est présent pour des raisons historiques.
 */
Druide::Druide( const bool& UI )
  : Classe( 0,  Pretres, "Duide" )
{
  if (!UI)
    {
      error() << "Création du personnage impossible" << endmsg;
      exit(1);
    }

  //NomClasse = "Druide";

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/**
 * Constructeur de "copie", destiné à être utilisé depuis MetaClasse.
 *
 * @param[in] ptr pointeur sur un objet de type Classe (MetaClasse en
 * fait).
 */
Druide::Druide( Classe *ptr )
  : Classe( *ptr )
{
  NomClasse = "Druide";

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

Druide::~Druide()
{}

void Druide::calculNiveau()
{
  if (XP < 7500)
    Niveau = XP / 2000 + 1;
  else if (XP < 12500)
    Niveau = 4;
  else if (XP < 20000)
    Niveau = 5;
  else if (XP < 35000)
    Niveau = 6;
  else if (XP < 125000)
    Niveau = (XP - 60000) / 30000 + 8;
  else if (XP < 200000)
    Niveau = 11;
  else if (XP < 300000)
    Niveau = 12;
  else if (XP < 3500000)
    {
      unsigned i;
	for (i = 0; i < 4; i++)
	  {
	    if (XP < (unsigned) 750000 * puissance(2, i))
	      break;
	  }
      Niveau = i + 12;
    }
}

// /**
//  * Il faut faire attention aux accents...
//  * @see afficheAccent
//  */
// void Druide::afficher_stats(ostream& os)
// {
//   os << "Druide" << endmsg;
//   for (unsigned i(0); i<6; i++)
//     os << Caracteristiques[i] << ' ' << Traits[i] << endmsg;
//   for (unsigned i(0); i<Competences.size(); i++)
//     os << afficheAccent(Comp[Competences[i]].Comp) << endmsg;
// }

// void Druide::appliquerModif(const string& type)
// {}

// void Druide::augmenterTalents(const unsigned& bonus, const bool& lvl_up)
// {}

void Druide::js(unsigned *tab) const
{
  *(tab + 0) = 13; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 14; // Bâton, batonnet baguette
  *(tab + 2) = 12; // Pétrification ou Métamorphose
  *(tab + 3) = 16; // Souffles
  *(tab + 4) = 15; // Sorts
  
  unsigned cat;
  if (Niveau <= 4)
    cat = 0;
  else  if (Niveau <= 8)
    cat = 1;
  else  if (Niveau <= 12)
    cat = 2;
  else  if (Niveau <= 16)
    cat = 3;
  else  if (Niveau <= 20)
    cat = 4;
  else
    cat = 5;

  *(tab + 0) -= cat;
  *(tab + 1) -= cat;
  *(tab + 2) -= cat;
  *(tab + 3) -= cat;
  *(tab + 4) -= cat;
}

/**
 * Le Druide n'a pas de restrictions.
 */
// string Druide::getCompetence(const unsigned& i) const
// {
//   if (getComp(i) < nbrCompetences)
//     return afficheAccent(Comp[getComp(i)].Comp);
//   else
//     return "Petit problème";
// }

unsigned Druide::calculeTAc0() const
{
  unsigned mod_niveau(2 * ((Niveau - 1) / 3));

  return 20 - mod_niveau;
}

void Druide::calculNivMag()
{
  if (Niveau < 16)
    NiveauMagique = (Niveau + 1) / 2;
  else
    NiveauMagique = 7;
}

/**
 * Les sorts disponibles pour un Druide sont donnés par son niveau et
 * les sphères auquelles il a accès.
 *
 * La variable Classe::fonction permet d'afficher :
 *
 * FUNC  : le nom du fichier qui va être écrit
 *
 * LINFO : la vérification que la compétence est bien une sphère, ou une
 * école de magie
 *
 * LFUNC : gestion des sphères élémentaires et des accents
 *
 * DEBUG : ouverture du fichier, 
 * 
 */
void Druide::ecrireSorts( const string& nom_fichier, const bool& ecrase,
			  const unsigned& sagesse ) const
{
  /*Debug::*/empileNiveau( &fonction );
  ofstream sortie;
  string nomComplet, nomFichier;
  func() << "A écrire : " << nom_fichier << endmsg;

  if (ecrase)
    /*
     * Ecrase l'éventuel contenu du fichier
     */
    {
      sortie.open(nom_fichier.c_str());
      debug() << "Ouverture                [ OK ]" << endmsg;
    }
  else
    /*
     * Alors on ouvre le fichier de telle sorte à écrire à la suite
     */
    {
      sortie.open(nom_fichier.c_str(), ios::app);
      debug() << "Ouverture                [ OK ]" << endmsg;
    }

  sortie << "\\section*{Sorts disponibles - Prêtre}" << endmsg;

  unsigned taille( tailleCompetences() );
  size_t loc;

  vector< unsigned > vec;

  sortsParNiveau( vec, sagesse );
  
  sortie << "\\begin{center}" << endl 
	 << "\\begin{tabular*}{10cm}{@{\\extracolsep{\\fill}} l r}"
	 << endl << "\\multicolumn{2}{c}{Nombre de sorts par jour} \\\\"
	 << "\\hline \\\\ \\vspace{1mm}" << endl;
  for (unsigned i(0); i < NiveauMagique; i++)
    sortie << "Niveau " << i+1 << " & " << vec[ i ] << " \\\\" << endl;
  sortie << "\\end{tabular*}" << endl << "\\end{center}" << endl;

  for (unsigned i(0); i < taille; i++)
    {
      if ( numCompetence( i ) > 2 )
	{
	  /*
	   * Ne pas oublier le 'true', pour garder la syntaxe LaTeX !
	   * sinon on a les accents écrits "en dur" et on ne peut plus
	   * rien faire pour rattraper le coup!
	   */
	  nomComplet = nomCompetence( i );
	  nomFichier = nomCompetence( i, true );
	  linfo() << "Check sphère : " << nomComplet << endmsg;
	  loc = nomComplet.find( " (m", 0 );
	  if ( loc == string::npos )
	    {
	      lfunc() << "Pas une sphère..." << endmsg;
	      continue;
	    }
	  
	  for (unsigned j(1); j <= NiveauMagique; j++)
	    {
	      if ( numCompetence( i ) < 71 && j > 3)
		/*
		 * Accès mineur => jusqu'au lvl 3 uniquement!
		 */
		break;
	      debug() << "accents loin " << nomFichier
		      << endmsg;
	      sortie << "\\input{../../Sorts_clerc/" << nomFichier << "_" << j
		     << ".inc.tex}" << endl;
	      
	    }	  
	}
    }

  sortie.close();
  /*Debug::*/depileNiveau();
}

void Druide::sortsParNiveau( vector< unsigned > & sorts,
			     const unsigned& sagesse ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Druide::sortsParNiveau -> début" << endmsg;

  vector< unsigned > tmp( 7, 0 );
  func() << "Nbr de niveaux de sort = " << NiveauMagique << endmsg;

  switch(Niveau)
    {
    case 1:
      tmp[0] = 1;
      break;
    case 2:
      tmp[0] = 2;
      break;
    case 3:
      tmp[0] = 2;
      tmp[1] = 1;
      break;
    case 4:
      tmp[0] = 3;
      tmp[1] = 2;
      break;
    case 5:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 1;
      break;
    case 6:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      break;
    case 7:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      tmp[3] = 1;
      break;
    case 8:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 2;
      break;
    case 9:
      tmp[0] = 4;
      tmp[1] = 4;
      tmp[2] = 3;
      tmp[3] = 2;
      tmp[4] = 1;
      break;
    case 10:
      tmp[0] = 4;
      tmp[1] = 4;
      tmp[2] = 3;
      tmp[3] = 3;
      tmp[4] = 2;
      break;
    case 11:
      tmp[0] = 5;
      tmp[1] = 4;
      tmp[2] = 4;
      tmp[3] = 3;
      tmp[4] = 2;
      if (sagesse >= 17)
      tmp[5] = 1;
      break;
    case 12:
      tmp[0] = 6;
      tmp[1] = 5;
      tmp[2] = 5;
      tmp[3] = 3;
      tmp[4] = 2;
      if (sagesse >= 17)
      tmp[5] = 2;
      break;
    case 13:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 4;
      tmp[4] = 2;
      if (sagesse >= 17)
      tmp[5] = 2;
      break;
    case 14:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 5;
      tmp[4] = 3;
      if (sagesse >= 17)
      tmp[5] = 2;
      if (sagesse >= 18)
      tmp[6] = 1;
      break;
    case 15:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 6;
      tmp[4] = 4;
      if (sagesse >= 17)
	tmp[5] = 2;
      if (sagesse >= 18)
	tmp[6] = 1;
      break;
    case 16:
      tmp[0] = 7;
      tmp[1] = 7;
      tmp[2] = 7;
      tmp[3] = 6;
      tmp[4] = 4;
      if (sagesse >= 17)
	tmp[5] = 3;
      if (sagesse >= 18)
	tmp[6] = 1;
      break;
    case 17:
      tmp[0] = 7;
      tmp[1] = 7;
      tmp[2] = 7;
      tmp[3] = 7;
      tmp[4] = 5;
      if (sagesse >= 17)
	tmp[5] = 3;
      if (sagesse >= 18)
	tmp[6] = 2;
      break;
    case 18:
      tmp[0] = 8;
      tmp[1] = 8;
      tmp[2] = 8;
      tmp[3] = 8;
      tmp[4] = 6;
      if (sagesse >= 17)
	tmp[5] = 4;
      if (sagesse >= 18)
	tmp[6] = 2;
      break;
    case 19:
      tmp[0] = 9;
      tmp[1] = 9;
      tmp[2] = 8;
      tmp[3] = 8;
      tmp[4] = 6;
      if (sagesse >= 17)
	tmp[5] = 4;
      if (sagesse >= 18)
	tmp[6] = 2;
      break;
    default:
      tmp[0] = 9;
      tmp[1] = 9;
      tmp[2] = 9;
      tmp[3] = 8;
      tmp[4] = 7;
      if (sagesse >= 17)
	tmp[5] = 5;
      if (sagesse >= 18)
	tmp[6] = 2;
      break;
    }

  sorts.assign( tmp.begin(), tmp.end() );
  sorts.resize( NiveauMagique );

  unsigned compteur1(0), compteur2(0), compteur3(0), compteur4(0), i;

  if (sagesse >= 13 && sagesse < 18)
    {
      compteur1 = (sagesse - 11) / 2;
      compteur2 = (sagesse - 12) / 2;
    }
  else if (sagesse >= 18 && sagesse < 19)
    {
      compteur1 = sagesse - 14;
      compteur2 = sagesse - 16;
    }
  else if (sagesse >= 19 && sagesse < 23)
    {
      compteur1 = (sagesse + 1) / 2 - 6;
      compteur2 = sagesse / 2 - 6;
      compteur3 = sagesse - 18;
    }
  else
    {
      compteur1 = (sagesse + 1) / 2 - 6;
      compteur2 = sagesse / 2 - 6;
      compteur3 = sagesse - 19;
      compteur4 = 1;
    }

  for (i = 0; i < compteur1; i++)
    {
      if (i == NiveauMagique)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur2; i++)
    {
      if (i == NiveauMagique)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur3; i++)
    {
      if (i == NiveauMagique)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur4; i++)
    {
      if (i == NiveauMagique)
	break;
      sorts[i]++;
    }

  func() << "Druide::sortsParNiveau -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont accessibles
 * à quel coût.
 *
 * Un Druide n'a qu'une sélection limitée d'armes, dont le coût est de 3 pp.
 */
void Druide::prixAcces(map<string, unsigned>*& Map) const
{
  /*Debug::*/empileNiveau( & fonction );
  func() << "Duide::prixAcces -> début" << endmsg;

  ExtraireClef< string, unsigned > extraitStr;
  vector< string > clefs( Map -> size() );
  EcrireElementConteneur< string, FlotMessage > ecrireStr( &lfunc() );
  
  transform( Map -> begin(), Map -> end(), clefs.begin(), extraitStr );
  for_each( clefs.begin(), clefs.end(), ecrireStr );

  Map -> operator[]("Druide") = 3;
  
  func() << "Druide::prixAcces -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

unsigned Druide::aSpecialisation() const
{
  return find( Competences.begin(), Competences.end(), 17 ) !=
    Competences.end() ? 1 : 0;
}
